#include "ses_timer.h"
#include "ses_scheduler.h"
#include <stdio.h>

/* A task consists of a task pointer and an expire value. */
typedef struct {
	task_t task; // pointer to task function
	uint16_t expire; // expire value (how many updates to wait)
} taskDescriptor_t;

/* Array that holds all scheduled tasks */
volatile taskDescriptor_t tasks[SCHEDULER_ENTRIES];
uint32_t currentTime;

void scheduler_update(void) {

	// every 1ms update the hour
	currentTime++;

	for (int i = 0; i < SCHEDULER_ENTRIES; i++) {
		if (tasks[i].expire != 0) {
			--tasks[i].expire;
		}
	}
}

void scheduler_init() {
	sei();

	currentTime = 0;

	// initialize scheduler w/ NULL tasks
	for (int i = 0; i < SCHEDULER_ENTRIES; i++) {
		taskDescriptor_t t = { .task = NULL, .expire = 0 };
		tasks[i] = t;
	}

	// start timer2 and begin updating each 1 ms
	timer2_init(scheduler_update);
}

void scheduler_run() {
	task_t task;
	while (1) {
		for (int i = 0; i < SCHEDULER_ENTRIES; i++) {
			if (tasks[i].expire == 0 && tasks[i].task != NULL) {
				cli();
				task = tasks[i].task;
				tasks[i].task = NULL;
				sei();
				task();
			}
		}
	}
}

bool scheduler_add(task_t task, uint16_t expire) {
	for (int i = 0; i < SCHEDULER_ENTRIES; i++) {
		if (tasks[i].task == NULL) {
			tasks[i].task = task;
			tasks[i].expire = expire;
			return true;
		}
	}
	return false;
}

void scheduler_remove(task_t task) {
	for (int i = 0; i < SCHEDULER_ENTRIES; i++) {
		if (tasks[i].task == task) {
			tasks[i].task = NULL;
		}
	}
}

uint32_t scheduler_getTime() {
	return currentTime;
}

void scheduler_setTime(uint32_t time) {
	cli();
	currentTime = time;
	sei();
}

void scheduler_getTimeRTC(type_timeRTC *time) {
	uint32_t tempTime;
	cli();
	tempTime = currentTime;
	sei();

	if (time == NULL)
		return;

	time->hour = (tempTime / MILLI_TO_HOUR) % HOUR_REMAINDER;
	time->minute = (tempTime / MILLI_TO_MIN) % MIN_REMAINDER;
	time->second = (tempTime / MILLI_TO_SEC) % SEC_REMAINDER;
	time->milli = (tempTime % MILLI_REMAINDER);

}

void scheduler_setTimeRTC(const type_timeRTC *time) {
	uint32_t tempTime = 0;

	//check if timer is set
	if (time == NULL)
		return;
	if ((time->hour >= 24) || (time->minute >= 60) || (time->second >= 60)
			|| (time->milli >= 1000))
		return;

	tempTime += time->milli;
	tempTime += time->second * SEC_TO_MILLI;
	tempTime += time->minute * MILLI_TO_MIN;
	tempTime += time->hour   * MILLI_TO_HOUR;

	cli();
	currentTime = tempTime;
	sei();
}
